from mydefine import *
import os.path
import hashlib
import datetime
import shutil

def file_hash(file_path: str, hash_method) -> str:
    if not os.path.isfile(file_path):
        print(f'文件 "{file_path}" 不存在')
        return ''
    h = hash_method()
    with open(file_path, 'rb') as f:
        while b := f.read(8192):
            h.update(b)
    return h.hexdigest()


def file_md5(file_path: str) -> str:
    return file_hash(file_path, hashlib.md5)


def file_sha256(file_path: str) -> str:
    return file_hash(file_path, hashlib.sha256)


def repoFolder_search(repo_name, db):
    status, res = db.sqlexe(f'select * from tb_repo where name=="{repo_name}"')

    if os.path.exists(f'versionRepo/{repo_name}'):
        # TODO
        pass
    else:
        print(f'"{repo_name}" does not exsit')



date_fmt = '%Y/%m/%d %H:%M:%S'


def versionCreate(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
    repo_name = head_recved['repo_name']
    timestr = datetime.datetime.now().strftime(date_fmt)

    # 插入数据库
    status,_ = db.sqlexe(f'insert into tb_repo (name,create_time) values ("{repo_name}","{timestr}")')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    status,res = db.sqlexe(f'select id from tb_repo where name=="{repo_name}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    # 创建库文件夹
    print(f'new repo id = {res[0][0]}')
    repo_id = res[0][0]
    if os.path.exists(f'versionRepo/{repo_id}') is False:
        os.mkdir(f'versionRepo/{repo_id}')

    print(f'version repo "{repo_name}", created at "{timestr}"')
    head_tosend = {"type": RESPONSE_VERSIONCREATE, "status":"", "models": {}}
    return head_tosend



def versionPull(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
    status, repo_res = db.sqlexe(f'select id,name,create_time,cur_version from tb_repo')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    val_repos = []
    for (id,name,create_time,cur_version) in repo_res:
        val_repo = {
            "id":           str(id),
            "name":         str(name),
            "create_time":  str(create_time),
            "cur_version":  str(cur_version),
        }
        val_repos.append(val_repo)

    status, version_res = db.sqlexe(f'select name,hash,repo,time from tb_version')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    version_map= {} # 每个key对应一个repo, 每个val对应该repo的version list
    for (name, hash, repo, time) in version_res:
        if repo not in version_map.keys():
            version_map[repo] = []
        version_map[repo].append({
            "name": str(name),
            "hash": str(hash),
            "repo": str(repo),
            "time": str(time),
        })

    for ver in version_map:
        version_map[ver].sort(
            key=lambda ele: datetime.datetime.strptime(str(ele["time"]), date_fmt),
            reverse=True)

    models = {
        'titles': ['repo', 'version'],
        'values': [[val_repos, version_map]]
    }

    head_tosend = {"type": RESPONSE_VERSIONPULL, "status":"", "models": models}
    return head_tosend



def versionAdd(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}

    ver_name = head_recved["name"]
    ver_hash = file_md5('test3.db')
    ver_repo = head_recved["repo"] # repo id
    ver_time = datetime.datetime.now().strftime(date_fmt)

    # 检测是否存在相同文件
    status, version_res = db.sqlexe(f'select * from tb_version where repo=="{ver_repo}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    if (len(version_res) > 0):
        l_version_res = list(version_res)
        l_version_res.sort(
            key=lambda ele: datetime.datetime.strptime(str(ele[3]), date_fmt),
            reverse=True)
        if (l_version_res[0][1] == ver_hash):
            print("文件无更改, 无需添加")
            return {"type": RESPONSE_VERSIONADD, "status": f'文件无更改, 无需添加'}

    # 复制当前文件至版本库文件夹
    shutil.copy('test3.db', f'versionRepo/{ver_repo}/{ver_hash}')

    # 插入数据库
    status, _ = db.sqlexe(f'insert into tb_version (name,hash,repo,time) values ("{ver_name}","{ver_hash}","{ver_repo}","{ver_time}")')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    # 更新版本库最新版本
    status, _ = db.sqlexe(f'update tb_repo set cur_version="{ver_hash}" where id=={ver_repo}')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    print(f'version "{ver_hash}", created at "{ver_time}"')
    head_tosend = {"type": RESPONSE_VERSIONADD, "status":"", "models": {}}
    return head_tosend



def versionSync(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}
    tar_repo_id = head_recved['tar_repo_id']
    tar_hash = head_recved['tar_hash']
    # 查询版本
    status, version_res = db.sqlexe(f'select hash from tb_version where hash=="{tar_hash}" and repo="{tar_repo_id}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    if len(version_res) == 0:
        return {"type": RESPONSE_VERSIONSYNC, "status":f'版本库(ID)"{tar_repo_id}"不存在版本"{tar_hash}"', "models": {}}

    # 检测文件
    if os.path.exists(f'versionRepo/{tar_repo_id}/{tar_hash}') is False:
        return {"type": RESPONSE_VERSIONSYNC, "status":f'版本库(ID)"{tar_repo_id}"版本"{tar_hash}"缓存文件不存在', "models": {}}

    # 复制文件
    shutil.copy(f'versionRepo/{tar_repo_id}/{tar_hash}', 'test3.db')

    head_tosend = {"type": RESPONSE_VERSIONSYNC, "status":"", "models": {}}
    return head_tosend



def versionQuery(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}

    cur_hash = file_md5('test3.db')

    status, version_res = db.sqlexe(f'select name, hash, repo, time from tb_version where hash=="{cur_hash}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    val_vers = []
    for (name, hash, repo, time) in version_res:
        val_vers.append([name, hash, repo, time])

    status, repo_res = db.sqlexe(f'select id,name from tb_repo')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    id_map = {}
    for (id, name) in repo_res:
        id_map[id] = name

    for ver in val_vers:
        ver[2] = id_map[ver[2]]

    val_vers.sort(
        key=lambda ele: datetime.datetime.strptime(str(ele[3]), date_fmt),
        reverse=True)

    models = {
        'titles': ['name', 'hash', 'repo_name', 'create_time'],
        'values': val_vers
    }

    print(val_vers)

    head_tosend = {"type": RESPONSE_VERSIONQUERY, "status":"", "models": models}
    return head_tosend


def versionDelete(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}

    tar_repo_id = head_recved['tar_repo_id']
    tar_hash = head_recved['tar_hash']

    # 删除数据库记录
    status, _ = db.sqlexe(f'delete from tb_version where hash=="{tar_hash}" and repo="{tar_repo_id}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    # 检测是否删除的是当前版本库的HEAD
    status, repo_res = db.sqlexe(f'select id from tb_repo where id="{tar_repo_id}" and cur_version="{tar_hash}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    if len(repo_res) > 0:
        # 查询上一个版本
        status, version_res = db.sqlexe(f'select hash,time from tb_version where repo=="{tar_repo_id}"')
        if (None != status):
            return {"type": RESPONSE_ERROR, "status": status}
        if len(version_res) == 0: # 没有更早的版本了
            # HEAD置空
            status, _ = db.sqlexe(f'update tb_repo set cur_version="" where id=="{tar_repo_id}"')
            if (None != status):
                return {"type": RESPONSE_ERROR, "status": status}
        else:
            version_map = [[hash, time] for (hash, time) in version_res]
            # 时间降序
            version_map.sort(key=lambda ele : datetime.datetime.strptime(str(ele[1]), date_fmt), reverse=True)
            # 更新HEAD
            status, _ = db.sqlexe(f'update tb_repo set cur_version="{version_map[0][0]}" where id=="{tar_repo_id}"')
            if (None != status):
                return {"type": RESPONSE_ERROR, "status": status}

    # 检测文件
    if os.path.exists(f'versionRepo/{tar_repo_id}/{tar_hash}') is False:
        return {"type": RESPONSE_VERSIONDELETE, "status":f'版本库(ID)"{tar_repo_id}"版本"{tar_hash}"缓存文件不存在', "models": {}}

    # 删除文件
    os.remove(f'versionRepo/{tar_repo_id}/{tar_hash}')

    head_tosend = {"type": RESPONSE_VERSIONDELETE, "status":"", "models": {}}
    return head_tosend



def versionRollback(head_recved, db):
    block_size = head_recved['block_size']
    if (0 == len(block_size)):
        head_tosend = {"type": RESPONSE_ERROR, "status": "没有数据！"}

    tar_repo_id = head_recved['tar_repo_id']
    tar_hash = head_recved['tar_hash']

    # 查询版本
    status, version_res = db.sqlexe(f'select hash,time from tb_version where repo=="{tar_repo_id}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}
    if len(version_res) == 0:
        return {"type": RESPONSE_VERSIONROLLBACK, "status":f'版本库(ID)"{tar_repo_id}"不存在版本"{tar_hash}"', "models": {}}

    # 更改目标库的HEAD
    status, _ = db.sqlexe(f'update tb_repo set cur_version="{tar_hash}" where id=="{tar_repo_id}"')
    if (None != status):
        return {"type": RESPONSE_ERROR, "status": status}

    # 找到较目标版本晚的版本
    version_map = [[hash, time] for (hash, time) in version_res]
    # 降序
    version_map.sort(key=lambda ele : datetime.datetime.strptime(str(ele[1]), date_fmt), reverse=True)
    print(version_map)
    l_hash_to_remove = []
    l_file_to_remove = []

    for [hash, time] in version_map:
        if hash == tar_hash:
            break
        l_hash_to_remove.append([hash])
        l_file_to_remove.append(hash)
    
    # 删除数据库记录
    if len(l_hash_to_remove) > 0:
        status, _ = db.sqlexe(f'delete from tb_version where hash==? and repo="{tar_repo_id}"', l_hash_to_remove)
        if (None != status):
            return {"type": RESPONSE_VERSIONROLLBACK, "status": status}

    # 检测并删除文件
    for hash in l_file_to_remove:
        if os.path.exists(f'versionRepo/{tar_repo_id}/{hash}') is False:
            return {"type": RESPONSE_VERSIONROLLBACK, "status":f'版本库(ID)"{tar_repo_id}"版本"{hash}"缓存文件不存在', "models": {}}
        os.remove(f'versionRepo/{tar_repo_id}/{hash}')
    head_tosend = {"type": RESPONSE_VERSIONROLLBACK, "status":"", "models": {}}
    return head_tosend
